import agents, graphics, time, math, pygame
from gameObject import GameObject
from bullet import Bullet
from explosions import Explosion

class TankAction:
    """
    TankAction is a concrete class providing means to set all the necessary variables
    for updating Tank's state variables each loop of the game.
    """
    def __init__(self):
        self.forward = False
        self.reverse = False
        self.left = False
        self.right = False
        self.shotThisAction = False
        self.shoot = False
        self.turretAngle = "FIX"

    def turnLeft(self):
        self.left = True
        self.right = False

    def turnRight(self):
        self.right = True
        self.left = False
        
    def goForward(self):
        self.forward = True
        self.reverse = False

    def goReverse(self):
        self.reverse = True
        self.forward = False

    def shootBullet(self):
        if self.shotThisAction == False:
            self.shoot = True   
            self.shotThisAction = True
        else:
            self.shoot = False
            self.shotThisTurn = False

    def setTurretAngle(self, angle):
        self.turretAngle = angle

    def idle(self):
        self.__init__()

class Tank( GameObject ):
    """
    Tank is a tank GameObject. *cough*
    """
    INDEX = 1
    MAX_AMMO = 5
    SHOOT_INTERVAL = 0.25 # In seconds.
    RELOAD_INTERVAL = 1.5 # In seconds.
    SPEED = 100 # In pixels/s.
    ANGLE_SPEED = 2.0 # In radians/s.
    
    MAX_HP = 4.0

    def __init__(self, position, speed, angle, game, agentName="KeyboardAgent", team="green"):
        # First initiate GameObject since this is a new __init__.
        GameObject.__init__(self)
        ########################
        # TANK STATE VARIABLES #
        ########################.

        # "Identity" variables.
        self.index = Tank.INDEX
        Tank.INDEX += 1
        if agentName not in dir(agents):
            raise AttributeError, agentName + ' is not a class in agents.py.'
        self.agentClassName = getattr(agents, agentName)
        self.agentClass = self.agentClassName(self.index)
        self.team = team
        self.game = game
        self.collisionGroups = [self.game.tanks, self.game.lowObstacles, self.game.highObstacles]
        # Positioning variables.
        self.x, self.y = position
        self.speed = Tank.SPEED
        self.angle = angle # In radians.
        self.turretAngle = 0.0
        self.angleSpeed = Tank.ANGLE_SPEED
        # HP/shooting variables.
        self.hp = 2.0
        self.ammo = Tank.MAX_AMMO
        self.shoot = False
        self.shotBefore = False
        # Timing variables.
        self.creationTime = time.time()
        self.destrucitonTime = 0.0
        self.shootTime = self.creationTime
        self.reloadTime = self.creationTime
        # Keep a copy of the action and dt.
        self.action = TankAction()
        self.dt = 0.0

        ###########################
        # TANK GRAPHICS VARIABLES #
        ###########################

        # Load the correct sprite images for later transformation.    
        self.spriteBody = graphics.loadImage("sprites/"+self.team+"Body00.png")
        self.spriteTurret = graphics.loadImage("sprites/"+self.team+"Turret.png")
        # Transform the images.
        self.image = pygame.transform.rotate(self.spriteBody, math.degrees(self.angle))
        self.imageTurret = pygame.transform.rotate(self.spriteTurret, math.degrees(self.turretAngle))
        # Create rects from images and store a silly rect for the bars.
        self.rect = self.image.get_rect()
        self.rectTurret = self.imageTurret.get_rect()
        self.rect.center = (self.x, self.y)
        self.rectTurret.center = (self.x, self.y)
        self.rectBars = pygame.Rect(0,0,1,1)
        # Mask the images for collision.
        self.mask = pygame.mask.from_surface(self.image)

    def update(self, previousTime, currentTime):
        # Update the action and time-step.
        action = self.agentClass.getAction()
        dt = currentTime - previousTime
        self.dt = dt                                                       # Updated!
        # Calculate the positioning increments.
        dx = 0
        dy = 0
        da = 0
        if action.forward == True:
            dx += math.cos(self.angle) * dt * self.speed
            dy -= math.sin(self.angle) * dt * self.speed
        elif action.reverse == True:
            dx -= math.cos(self.angle) * dt * self.speed
            dy += math.sin(self.angle) * dt * self.speed
        if action.left == True:
            action.forward = False
            da += dt * self.angleSpeed
        elif action.right == True:
            da -= dt * self.angleSpeed
        # Create positioning variables.
        x = self.x + dx
        y = self.y + dy      
        x, y = self.boundaryConditions(x, y)
        angle = self.angle + da
        turretAngle = self.action.turretAngle                        
        # Check for collisions at the new position.
        collisionSprite = GameObject()
        collisionSprite.image = pygame.transform.rotate(self.spriteBody, math.degrees(angle))
        collisionSprite.rect = collisionSprite.image.get_rect()
        collisionSprite.rect.center = (x, y)
        collisionSprite.mask = pygame.mask.from_surface(collisionSprite.image)
        # If there is no collision, allow the move.
        if len( collisionSprite.detectCollision(self.collisionGroups) ) == 1:
            self.x = x                                                     # Updated!
            self.y = y                                                     # Updated!
            self.angle = angle                                             # Updated!
            if turretAngle == "FIX":
                self.turretAngle = self.angle
            else:
                self.turretAngle = turretAngle
            self.image = collisionSprite.image                             # Updated!
            self.rect = collisionSprite.rect                               
            self.rect.center = (self.x, self.y)                            # Updated!
            self.mask = collisionSprite.mask                               # Updated!
        # Always update the turret. We want lethal immobile tanks!
        self.imageTurret = pygame.transform.rotate(self.spriteTurret, math.degrees(self.turretAngle))
        self.rectTurret = self.imageTurret.get_rect()
        self.rectTurret.center = (self.x, self.y)
        # Create shooting variables.
        # Disable instant first reload.
        if self.shotBefore == False:
            self.reloadTime = currentTime
            self.shotBefore = True
        # Shoot and reload.
        if action.shoot == True:
            shootCooldown = currentTime - self.shootTime
            if (shootCooldown >= Tank.SHOOT_INTERVAL and self.ammo > 0):
                self.shoot = True                                          # Updated!
                self.ammo -= 1                                             # Updated!
                self.shootTime = currentTime                               # Updated!
                self.game.bullets.add( Bullet(self.x, self.y, self.turretAngle, self.team, self.game) )
            else:
                self.shoot = False
        reloadCooldown = currentTime - self.reloadTime
        if (reloadCooldown >= Tank.RELOAD_INTERVAL and self.ammo < Tank.MAX_AMMO):
            self.ammo += 1                                                 # Updated!
            self.reloadTime = currentTime                                  # Updated!
        # If we are dead, remove us from tanks and explode!
        if self.hp <= 0:
            self.clear()
            self.game.tanks.remove(self)
            self.game.explosions.add( Explosion(self.x, self.y, self.angle, currentTime, self.game, kind="tank") )

    def clear(self):
        self.game.display.blit(self.game.background, self.rect, self.rect)
        self.game.display.blit(self.game.background, self.rectTurret, self.rectTurret)
        self.game.display.blit(self.game.background, self.rectBars, self.rectBars)

    def draw(self):
        self.game.display.blit(self.image, self.rect)
        self.game.display.blit(self.imageTurret, self.rectTurret)
        self.drawBars()

    def boundaryConditions(self, x, y):
        """
        Returns (x,y) trimmed to fit inside the display based on error tolerance (in 
        pixels) and display size.
        """
        display_x = graphics.Graphics.WIDTH
        display_y = graphics.Graphics.HEIGHT
        new_x = x
        new_y = y
        if x > display_x:
            new_x = display_x
        elif x < 0:
            new_x = 0 
        if y > display_y:
            new_y = display_y
        elif y < 0:
            new_y = 0
        return (new_x, new_y)
    
    def drawBars(self):
        """
        Used for drawing the HP and ammo bars.
        """
        left = self.x + 16 + 2
        top = self.y - 32
        w = 5
        h = 16

        # Draw HP bars.
        ratio = float(self.hp) / float(Tank.MAX_HP)
        pygame.draw.rect(self.game.display,
                         graphics.white,
                         (left + 1, math.floor(top + 1 + (1 - ratio)*(h-2)), w - 2, math.ceil(ratio*(h-2))),
                         0)
        pygame.draw.rect(self.game.display,
                         graphics.green,
                         (left, top, w, h),
                         1)

        # Draw ammo bars.
        ratio = float(self.ammo) / float(Tank.MAX_AMMO)
        pygame.draw.rect(self.game.display,
                         graphics.white,
                         (left +  w + 1, math.floor(top + 1 + (1 - ratio)*(h-2)), w - 2, math.ceil(ratio*(h-2))),
                         0)
        pygame.draw.rect(self.game.display,
                         graphics.red,
                         (left + w, top, w, h),
                         1)

        self.rectBars = pygame.Rect(left, top, 2*w, h)
                
